package edu.umassd.hciproject;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Vector;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class StoreSearchActivity extends Activity implements OnClickListener {

	Button bAdd, bRemove, bGo;
	ImageButton bSearch, bPick;
	AutoCompleteTextView addressBox;
	Geocoder geocoder;
	AddressSearch addressSearch;
	ListView resultsDisplay;// selectionDisplay;
	Spinner spRange;
	TextView tvSelectStores;
	int range = 10000;	// maximum store distance range in meters
	int[] ranges;
	String[] storeLocations, storeNames, rangeNames;
	ArrayList<String> placesNames;
	MyStorageManager pManager;
	public final static String STORES_SELECTION_KEY = "stores";
	static ArrayAdapter<String> listAdapter = null;
	//selectionAdapter = null;
		
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_store_search);
		
		geocoder = new Geocoder(getBaseContext(),Locale.getDefault());
		pManager = new MyStorageManager(this, MyPlacesActivity.MY_PLACES_FILENAME);
		placesNames = pManager.getNames();

		storeLocations = getResources().getStringArray(R.array.store_locations);
		storeNames = getResources().getStringArray(R.array.store_names);
		ranges = getResources().getIntArray(R.array.range_values_array);
		rangeNames = getResources().getStringArray(R.array.ranges_array);

		addressBox = (AutoCompleteTextView) findViewById(R.id.tvSearchLocation);
		
		addressSearch  = new AddressSearch();
		addressBox.setOnEditorActionListener(addressSearch);
		addressBox.setOnItemClickListener(new AdapterView.OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View v, int position,
					long id) {
				// TODO Auto-generated method stub
				addressBox.setText((String) pManager.getContentsOf((String) addressBox.getAdapter().getItem(position)));
			}
		});
		addressBox.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, placesNames));
		
		
		if (listAdapter == null)
			listAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice);
		
		resultsDisplay = (ListView) findViewById(R.id.lvStoreResults);
		resultsDisplay.setAdapter(listAdapter);
		resultsDisplay.setItemsCanFocus(false);
		resultsDisplay.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

		spRange = (Spinner)	findViewById(R.id.spRange);
		spRange.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {

			@Override
			public void onItemSelected(AdapterView<?> parent, View v,
					int position, long id) {
				// TODO Auto-generated method stub 
				range = ranges[position];
				addressSearch.searchAddress(addressBox.getText().toString());
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				// TODO Auto-generated method stub
			}
		});
		
		bGo = (Button) findViewById(R.id.bGoToStores);
		bGo.setOnClickListener(this);
		
		bSearch = (ImageButton) findViewById(R.id.ibSearchLocation);
		bSearch.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				addressSearch.searchAddress(addressBox.getText().toString());
			}
		});
		
		bPick = (ImageButton) findViewById(R.id.ibPickLocation);
		bPick.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				AlertDialog.Builder builder = new AlertDialog.Builder(StoreSearchActivity.this);
				
				builder.setItems(placesNames.toArray(new String[1]), new DialogInterface.OnClickListener() {
					
					@Override
					public void onClick(DialogInterface dialog, int which) {
						// TODO Auto-generated method stub
						addressBox.setText((String) pManager.getContentsOf(placesNames.get(which)));
					}
				});
				builder.show();
			}
		});
		
		tvSelectStores = (TextView) findViewById(R.id.tvSelectStores);
		tvSelectStores.setVisibility(View.INVISIBLE);

	}

	private void transferCheckedItems(ListView source, ListView dest) {
		// TODO Auto-generated method stub
		int count = source.getAdapter().getCount();
		ArrayAdapter<String> sourceAdapter = (ArrayAdapter<String>) source.getAdapter();
		ArrayAdapter<String> destAdapter = (ArrayAdapter<String>) dest.getAdapter();
		Vector<String> removed = new Vector<String>();	// holds items to remove
		for (int i=0; i<count; i++)
		{
			if (source.isItemChecked(i))
			{
				removed.add(sourceAdapter.getItem(i));	// marks item to remove
				destAdapter.add(sourceAdapter.getItem(i));	// restores item to resultsDisplay
			}
		}
		destAdapter.notifyDataSetChanged();
		// Remove marked items
		for (int i=0; i<removed.size();i++)
		{
			sourceAdapter.remove(removed.elementAt(i));
		}
		sourceAdapter.notifyDataSetChanged();
		count = source.getCount();
		for (int i=0;i<count;i++)
		{
			source.setItemChecked(i, false);
		}				
		count = dest.getCount();
		for (int i=0;i<count;i++)
		{
			dest.setItemChecked(i, false);
		}				
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_store_search, menu);
		return true;
	}

	/* NOT USED, BUT MAY BE USEFUL LATER
	 * Nirav Dangi @
	 * http://stackoverflow.com/questions/3574644/how-can-i-find-the-latitude-and-longitude-from-address
	 */
	/*
	public static JSONObject getLocationInfo(String address) {
        StringBuilder stringBuilder = new StringBuilder();
        try {

        address = address.replaceAll(" ","%20");    

        HttpPost httppost = new HttpPost("http://maps.google.com/maps/api/geocode/json?address=" + address + "&sensor=false");
        HttpClient client = new DefaultHttpClient();
        HttpResponse response;
        stringBuilder = new StringBuilder();


            response = client.execute(httppost);
            HttpEntity entity = response.getEntity();
            InputStream stream = entity.getContent();
            int b;
            while ((b = stream.read()) != -1) {
                stringBuilder.append((char) b);
            }
        } catch (ClientProtocolException e) {
        } catch (IOException e) {
        }

        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject = new JSONObject(stringBuilder.toString());
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return jsonObject;
    }
    */
	
	/* NOT USED, BUT MAY BE USEFUL LATER
	 * Nirav Dangi @
	 * http://stackoverflow.com/questions/3574644/how-can-i-find-the-latitude-and-longitude-from-address
	 */
	/*
  	public static Address getLatLong(JSONObject jsonObject) {
 
		Address address;
		double longitude, latitude;

        try {

            longitude = ((JSONArray)jsonObject.get("results")).getJSONObject(0).getJSONObject("geometry").getJSONObject("location").getDouble("lng");

            latitude = ((JSONArray)jsonObject.get("results")).getJSONObject(0).getJSONObject("geometry").getJSONObject("location").getDouble("lat");

        } catch (JSONException e) {
            return null;

        }
        
        address = new Address(Locale.getDefault());        
        address.setLongitude(longitude);
        address.setLatitude(latitude);

        return address;
    }
*/	
	public void showErrorDialog(String message)
	{
		AlertDialog alert = new AlertDialog.Builder(StoreSearchActivity.this).create();
		alert.setTitle("Sorry!");
		alert.setMessage(message);
		alert.setCancelable(true);
		alert.setButton(AlertDialog.BUTTON_NEGATIVE, "Ok", new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// TODO Auto-generated method stub
				dialog.dismiss();
			}
		});
		alert.setCanceledOnTouchOutside(true);
		alert.show();
	}

	@Override
	public void onClick(View v) {
		// bGo callback
		Intent intent = new Intent(this,StoreMapActivity.class);
		intent.putStringArrayListExtra(STORES_SELECTION_KEY , getSelectedStores());
		startActivity(intent);
	}


	
	private ArrayList<String> getSelectedStores() {
		// TODO Auto-generated method stub
		ArrayList<String> names = new ArrayList<String>();
		SparseBooleanArray checked = resultsDisplay.getCheckedItemPositions();
		for (int i=0; i<listAdapter.getCount(); i++) {
			if (checked.get(i))
				names.add(listAdapter.getItem(i));
		}

		return names;
	}



	class AddressSearch implements DialogInterface.OnClickListener, TextView.OnEditorActionListener, AddressSearchTask.OnSearchFinishedListener {
		CharSequence[] locationStrings;
		List<Address> locationResults;
		List<Address> storeResults;
		Address originAddress;
		
		@Override
		public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
			// TODO Auto-generated method stub
			
			boolean handled = false;
			if (actionId == EditorInfo.IME_ACTION_SEARCH)
			{
				String name = v.getText().toString();
				if (name.length() > 0){
					searchAddress(name);
				}
				else {
					Toast t = Toast.makeText(StoreSearchActivity.this, "No address entered", Toast.LENGTH_SHORT);
					t.show();
				}
				handled = true;
			}
			return handled;
		}

		// Handler for clicking on item from dialog that shows location results after search
		@Override
		public void onClick(DialogInterface d, int item) {
			// TODO Auto-generated method stub
			
			// Dismiss dialog
			d.dismiss();
			
			showStoreOptions(item);
		}
		
		private void showStoreOptions(int item) {
			// TODO Auto-generated method stub

			// Save selected location to variable
			originAddress = locationResults.get(item);

			// Change views
			addressBox.setText(locationStrings[item]);
			listAdapter.clear();
			listAdapter.notifyDataSetChanged();
//			selectionAdapter.clear();
			//selectionAdapter.notifyDataSetChanged();
			
			// Hide keyboard
			InputMethodManager imm = (InputMethodManager) getSystemService(
				    INPUT_METHOD_SERVICE);
			imm.hideSoftInputFromWindow(addressBox.getApplicationWindowToken(), 0);
			
			// Try to find stores
			ArrayList<Integer> stores = null;
			try {
				stores = getStoresInRange();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				Toast t = Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_LONG);
				t.show();
				return;
			}
			if (stores.isEmpty())
			{
				showErrorDialog("No available store found within " + range + " meters range");
				tvSelectStores.setVisibility(View.INVISIBLE);
				return;
			}
			
			// Show stores found
			tvSelectStores.setVisibility(View.VISIBLE);
			for (int i=0; i<stores.size(); i++)
			{
				listAdapter.add(storeNames[stores.get(i)]);
			}
			
			// Update views
			listAdapter.notifyDataSetChanged();
			addressBox.clearFocus();
			resultsDisplay.requestFocus();
			
		}

		private ArrayList<Integer> getStoresInRange() throws Exception {
			ArrayList<Integer> stores = new ArrayList<Integer>();
			for (int i=0; i<storeLocations.length; i++)
			{
				Address storeAddress = getStoreAddress(storeLocations[i]);
				float[] distances = new float[1];
				Location.distanceBetween(originAddress.getLatitude(), originAddress.getLongitude(), storeAddress.getLatitude(), storeAddress.getLongitude(), distances);
				if (distances[0] < range)
				{
					stores.add(i);
					//listAdapter.add(storeAddress.getAddressLine(0));
				}
			}
			return stores;
		}

		private Address getStoreAddress(String storeLocation) throws Exception {
			// TODO Auto-generated method stub
			List<Address> possibilities = null;
			
			possibilities = geocoder.getFromLocationName(storeLocation, 10);

			if (possibilities.isEmpty())
				throw new Exception("Store address not found");
			
			return possibilities.get(0);
		}

		public void searchAddress(String txt) {
			// TODO Auto-generated method stub
			
			/*
			if (!geocoder.isPresent())	// Unavailable on API level 8
			{
				AlertDialog alert = new AlertDialog.Builder(StoreSearchActivity.this).create();
				alert.setTitle("Error");
				alert.setMessage("Unable to search for location. Please, check network connection.");
				alert.show();
				return;
			}
			*/
			if (txt.length() == 0)
				return;
			
			if (!AppStatus.getInstance(StoreSearchActivity.this).isOnline()) {
				showErrorDialog("Internet connection not found. Unable to search for addresses.");
				return;
			}
			AddressSearchTask task = new AddressSearchTask(StoreSearchActivity.this);
			task.setOnSearchFinishedListener(this);
			task.execute(txt);
		}

		
		@Override
		public void onSearchFinished(List<Address> results, IOException e) {
			// TODO Auto-generated method stub
			if (e != null) {
				Toast t = Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_LONG);
				t.show();
				return;
			}
			locationResults = results;
			if (locationResults.size() > 0)
			{
				locationStrings = new String[locationResults.size()];
				for (int r=0; r<locationResults.size(); r++)
				{
					String display = "";
					for (int i=0; i<locationResults.get(r).getMaxAddressLineIndex(); i++)
					{	
						display += locationResults.get(r).getAddressLine(i) + "\n";
					}
					locationStrings[r] = display;
				}
				
				if (locationResults.size() == 1)
				{
					showStoreOptions(0);
				}
				else
				{
					AlertDialog.Builder builder = new AlertDialog.Builder(StoreSearchActivity.this).setItems(locationStrings, AddressSearch.this);
					builder.setTitle("Select an option");
					AlertDialog alert = builder.create();
					alert.show();
				}
			}
			else
			{
				showErrorDialog("Sorry! No location found");
			}
		}
	}
	


}
